package com.yugao.fintech.antelope.pay.websocket;

import com.alibaba.fastjson2.JSON;
import com.yugao.fintech.framework.core.response.Resp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * WebSocket服务类
 * /ws/payOrder/{订单号}/{客户端自定义ID}
 */
@Slf4j
@ServerEndpoint(value = "/ws/payOrder/{payOrderNo}/{cid}")
@Component
public class WsPayOrderServer {

    /** 当前在线客户端 数量 */
    private static int onlineClientSize = 0;

    /** payOrderId 与 WsPayOrderServer 存储关系, ConcurrentHashMap保证线程安全 */
    private static final Map<String, Set<WsPayOrderServer>> wsOrderIdMap = new ConcurrentHashMap<>();

    /** 与某个客户端的连接会话，需要通过它来给客户端发送数据 */
    private Session session;

    /** 客户端自定义ID */
    private String cid = "";

    /** 支付订单号 */
    private String payOrderNo = "";

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("payOrderNo") String payOrderNo, @PathParam("cid") String cid) {
        try {
            //设置当前属性
            this.cid = cid;
            this.payOrderNo = payOrderNo;
            this.session = session;

            Set<WsPayOrderServer> wsServerSet = wsOrderIdMap.get(payOrderNo);
            if(wsServerSet == null) {
                wsServerSet = new CopyOnWriteArraySet<>();
            }
            wsServerSet.add(this);
            wsOrderIdMap.put(payOrderNo, wsServerSet);

            addOnlineCount(); //在线数加1
            log.info("cid[{}],payOrderNo[{}]连接开启监听！当前在线人数为{}", cid, payOrderNo, onlineClientSize);
        } catch (Exception e) {
            log.error("ws监听异常cid[{}],payOrderNo[{}]", cid, payOrderNo, e);
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {

        Set<WsPayOrderServer> wsSet = wsOrderIdMap.get(this.payOrderNo);
        wsSet.remove(this);
        if(wsSet.isEmpty()) {
            wsOrderIdMap.remove(this.payOrderNo);
        }

        subOnlineCount(); //在线数减1
        log.info("cid[{}],payOrderNo[{}]连接关闭！当前在线人数为{}", cid, payOrderNo, onlineClientSize);
    }

    @OnError
    public void onError(Session session, Throwable error) {
        log.error("ws发生错误", error);
    }

    /**
     * 实现服务器主动推送
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

    /**
     * 根据订单ID,推送消息
     * 捕捉所有的异常，避免影响业务。
     * @param payOrderNo 支付订单号
     */
    public static void sendMsgByOrderNo(String payOrderNo, Resp<?> msg) {
        String msgJson = JSON.toJSONString(msg);
        try {
            log.info("推送ws消息到浏览器, payOrderNo={}，msg={}", payOrderNo, msgJson);


            Set<WsPayOrderServer> wsSet = wsOrderIdMap.get(payOrderNo);
            if(wsSet == null || wsSet.isEmpty()){
                log.info("payOrderNo[{}] 无ws监听客户端", payOrderNo);
                return ;
            }

            for (WsPayOrderServer item : wsSet) {
                try {
                    item.sendMessage(msgJson);
                } catch (Exception e) {
                    log.info("推送设备消息时异常，payOrderNo={}, cid={}", payOrderNo, item.cid, e);
                }
            }
        } catch (Exception e) {
            log.info("推送消息时异常，payOrderNo={}", payOrderNo, e);
        }
    }

    public static synchronized int getOnlineClientSize() {
        return onlineClientSize;
    }

    public static synchronized void addOnlineCount() {
        onlineClientSize++;
    }

    public static synchronized void subOnlineCount() {
        onlineClientSize--;
    }
}
